/**
 * 
 */
package ca.csf.dfc.poo.db;

import java.sql.*;



/**
 * Classe représentant la base de données, qui contient les "high scores" de chaque
 * type de robot pour chaque environnement, soit le total de leurs récompenses, avec la date
 * de la simulation où ont été enregistrés ces scores.
 * Cette classe utilise le pattern du Singleton.
 * 
 * @author Jean-François Bédard, Marie-Pier Chabot, 
 * Alexandre Paquet Fasys, Claude Roy
 */
public final class DbRecompenses {
	
	// Constantes pour la connexion à la base de données.
	/**
	 * Chaine pour le chargement en mémoire du driver
	 */
	private static final String JDBC_DRIVER = "org.sqlite.JDBC";
	/**
	 * Chaine de connexion, contenant le nom du fichier pour l'enregistrement
	 */
	private static final String CONNEXION_STRING = "jdbc:sqlite:recompenses.db";
	
	
	// Constantes pour la table qui contient les informations
	// à sauvegarder sur les robots.
	/**
	 * Nom de la table
	 */
	private static final String TABLE_RECOMPENSES = "Recompenses";
	/**
	 * Colonne contenant le nom de l'environnement
	 */
	private static final String COL_ENVIRONNEMENT = "environnement";
	/**
	 * Colonne contenant le meilleur score du robot intelligent
	 */
	private static final String COL_RECOMPENSES_INTELLIGENT = "recompenses_intelligent";
	/**
	 * Colonne contenant le meilleur score du robot vorace
	 */
	private static final String COL_RECOMPENSES_GREEDY = "recompenses_greedy";
	/**
	 * Colonne contenant le meilleur score du robot aléatoire
	 */
	private static final String COL_RECOMPENSES_RANDOM = "recompenses_random";
	/**
	 * Colonne contenant la date d'enregistrement de ces meilleurs scores
	 */
	private static final String COL_DATE= "date_simulation";
	
	
	
	// L'utilisation du mot clé volatile permet, en Java version 5 et supérieur, 
	// d'éviter le cas où "Singleton.instance" est non-nul,
    // mais pas encore "réellement" instancié.
	/**
	 * Donnée représentant l'instance de la DbRecompenses.
	 */
    private static volatile DbRecompenses instance = null;
	
	/**
	 * Donnée représentant la connexion à la base de données.
	 */
	private Connection m_Connection = null;
	
	
	/**
     * Constructeur par défaut de la DbRecompenses.
     * 
	 * @throws SQLException Erreur SQL
	 * @throws ClassNotFoundException Erreur due à l'absence du driver SqliteJDBC dans le classpath
     */
    private DbRecompenses() throws ClassNotFoundException, SQLException {
        super();
        
    	// Connexion à la BD
		Connection conn = this.getConnection();
		// Données dans la BD
		DatabaseMetaData dbm = conn.getMetaData();
		// On va chercher la table «recompenses»
		ResultSet tableRecompenses = dbm.getTables(null, null, DbRecompenses.TABLE_RECOMPENSES, null);
		
		// Si la table Robot n'existe pas (1ere utilisation), on la crée
		if(!tableRecompenses.next()){
			
			Statement st = conn.createStatement();
			
			String queryCreate = String.format(
					"CREATE TABLE %s (%s STRING PRIMARY KEY, %s, %s, %s, %s)",
					DbRecompenses.TABLE_RECOMPENSES,
					DbRecompenses.COL_ENVIRONNEMENT,
					DbRecompenses.COL_RECOMPENSES_INTELLIGENT,
					DbRecompenses.COL_RECOMPENSES_GREEDY,
					DbRecompenses.COL_RECOMPENSES_RANDOM,
					DbRecompenses.COL_DATE);
			
			st.executeUpdate(queryCreate);
			st.close();
		}
		
		tableRecompenses.close();
		fermerConnexionBD();
    }

    
    /**
     * Méthode qui renvoie une instance de la classe DbRecompenses.
     * 
     * @return Retourne l'instance de la DbRecompenses
     * @throws SQLException Erreur SQL
	 * @throws ClassNotFoundException Erreur due à l'absence du driver SqliteJDBC dans le classpath
     */
    public final static DbRecompenses getInstance() throws ClassNotFoundException, SQLException {
    	
        if (DbRecompenses.instance == null) {

           synchronized(DbRecompenses.class) {
        	   
             if (DbRecompenses.instance == null) {
            	 
            	 DbRecompenses.instance = new DbRecompenses();
             }
           }
        }
        
        return DbRecompenses.instance;
    }
    
    
    /**
     * Méthode pour obtenir les récompenses associées à chaque robot pour un environnement donné.
     * 
     * @param p_Environnement Le nom de l'environnement pour lequel on fournit les récompenses
     * @return Un tableau d'entier, contenant les récompenses (dans l'ordre) des types
     * 		   de robots : Intelligent, Greedy, Random
     * @throws SQLException Erreur SQL
	 * @throws ClassNotFoundException Erreur due à l'absence du driver SqliteJDBC dans le classpath
     */
    public int[] getRecompenses(String p_Environnement) throws ClassNotFoundException, SQLException{
    	
    	int[] tabRecompenses = {0,0,0};
        	
    	// Connexion à la BD
		Connection conn = this.getConnection();

		Statement st = conn.createStatement();
		
		// On va chercher la table «recompenses»
		ResultSet selectRobot = st.executeQuery(
				"SELECT * FROM " + DbRecompenses.TABLE_RECOMPENSES + " WHERE " + 
				DbRecompenses.COL_ENVIRONNEMENT +  " = '" + p_Environnement + "'");
		
		// On va chercher les données obtenues avec le SELECT
		while (selectRobot.next()){
			
			tabRecompenses[0] = selectRobot.getInt(2);
			tabRecompenses[1] = selectRobot.getInt(3);
			tabRecompenses[2] = selectRobot.getInt(4);
		}
		selectRobot.close();
		st.close();
		fermerConnexionBD();
		
		return tabRecompenses;    	
    }
    
    
    /**
     * Méthode pour obtenir la date de la simulation où les récompenses ont été mises à jour 
     * pour un environnement donné.
     * 
     * @param p_Environnement Le nom de l'environnement pour lequel on fournit les récompenses
     * @return Une chaine représentant la date en mémoire
     * @throws SQLException Erreur SQL
	 * @throws ClassNotFoundException Erreur due à l'absence du driver SqliteJDBC dans le classpath
     */
    public String getDate(String p_Environnement) throws ClassNotFoundException, SQLException{
        
    	String date = "ND";
    		
    	// Connexion à la BD
		Connection conn = this.getConnection();

		Statement st = conn.createStatement();
		
		// On va chercher la table «recompenses»
		ResultSet selectRobot = st.executeQuery(
				"SELECT * FROM " + DbRecompenses.TABLE_RECOMPENSES + " WHERE " + 
				DbRecompenses.COL_ENVIRONNEMENT +  " = '" + p_Environnement + "'");
		
		// On va chercher les données obtenues avec le SELECT
		while (selectRobot.next()){
			
			date = selectRobot.getString(5);
		}
		selectRobot.close();
		st.close();
		fermerConnexionBD();   
		
		return date;
    }
    
    
    /**
     * Méthode qui permet de sauvegarder les récompenses de chaque type de robot ainsi que la date
     * de la simulation pour un environnement donné.
     * 
     * @param p_Environnement Nom de l'environnement
     * @param p_tabRecompenses Tableau d'entiers contenant les récompenses, en ordre,
     * 						   des types de robots : Random, Greedy, Intelligent
     * @throws SQLException Erreur SQL
	 * @throws ClassNotFoundException Erreur due à l'absence du driver SqliteJDBC dans le classpath
     */
    public void saveRecompensesDate(String p_Environnement, int[] p_tabRecompenses, String p_Date) 
    		throws ClassNotFoundException, SQLException{
    	
    	boolean envExiste = false;
    	
    	// Connexion à la BD
		Connection conn = this.getConnection();

		Statement st = conn.createStatement();
		
		// On va chercher la table «recompenses»
		ResultSet selectRobot = st.executeQuery(
				"SELECT * FROM " + DbRecompenses.TABLE_RECOMPENSES + " WHERE " + 
				DbRecompenses.COL_ENVIRONNEMENT +  " = '" + p_Environnement + "'");
		
		// Vérifier si on a trouvé un enregistrement pour l'environnement donné
		if(selectRobot.next()){
			
			envExiste = true;
		}
		
		selectRobot.close();
		
		// Si on a déjà des données pour cette environnement, on fait un UPDATE
		if (envExiste){
			
			st.executeUpdate("UPDATE " + DbRecompenses.TABLE_RECOMPENSES + " SET " + 
							DbRecompenses.COL_RECOMPENSES_INTELLIGENT +  " = " + p_tabRecompenses[0] +
							", " + DbRecompenses.COL_RECOMPENSES_GREEDY + " = " +
							p_tabRecompenses[1] + ", " + DbRecompenses.COL_RECOMPENSES_RANDOM +
							" = " + p_tabRecompenses[2] + ", " + DbRecompenses.COL_DATE +
							" = '" + p_Date + "' WHERE " + DbRecompenses.COL_ENVIRONNEMENT +
							" = '" + p_Environnement + "'");
		}
		// Sinon, on fait un INSERT
		else{
			
			st.executeUpdate("INSERT INTO " + DbRecompenses.TABLE_RECOMPENSES + " VALUES ('" + 
							p_Environnement +  "'," + p_tabRecompenses[0] + "," +
							p_tabRecompenses[1] + "," + p_tabRecompenses[2] + ", '" + p_Date + "')");
		}
		
		st.close();
		fermerConnexionBD();
    }

    
	/**
	 * Méthode qui permet la connexion à la base de données.
	 * 
	 * @return La connexion
	 * @throws SQLException Erreur SQL
	 * @throws ClassNotFoundException Erreur due à l'absence du driver SqliteJDBC dans le classpath
	 */
	private Connection getConnection() throws SQLException, ClassNotFoundException{
		
		if(this.m_Connection == null || this.m_Connection.isClosed()){
			
			Class.forName(JDBC_DRIVER);
			this.m_Connection = DriverManager.getConnection(CONNEXION_STRING);
		}
		return this.m_Connection;
	}
	
	
	/**
	 * Méthode pour fermer la connexion à la base de données.
	 * 
	 * @throws SQLException Erreur SQL
	 */
	private void fermerConnexionBD() throws SQLException{
		
		if(m_Connection != null){
			
			if(!this.m_Connection.isClosed()){
				
				this.m_Connection.close();					
			}
		}
			
	}
}
